#include <stdio.h>

#include <util/mem.h>
#include <util/common_macros.h>
#include <util/msg.h>

#include "surface.h"
#include "triangle.h"
#include "rectangle.h"
/* ========================================================================== */

static boolean __surface_destroy(ImpObject * object) {
	ImpSurface * s;
	//ListIterator * it;

	s = IMP_SURFACE(object);

	/*
		it = s->faces->first;
		while (it) {
			imp_object_destroy(IMP_OBJECT(it->data));
			it = it->next;
		}
		it = s->edges->first;
		while (it) {
			imp_object_destroy(IMP_OBJECT(it->data));
			it = it->next;
		}
		it = s->vertices->first;
		while (it) {
			imp_object_destroy(IMP_OBJECT(it->data));
			it = it->next;
		}
	 */

	if (s->faces) {
		list_remove_all_data(s->faces);
		list_delete(s->faces);
	}
	if (s->edges) {
		list_remove_all_data(s->edges);
		list_delete(s->edges);
	}
	if (s->vertices) {
		list_remove_all_data(s->vertices);
		list_delete(s->vertices);
	}
	
	return TRUE;
}
static void __surface_class_init(ImpObjectClass * klass) {
	klass->object_clone = NULL;
	klass->object_destroy = __surface_destroy;
	klass->object_attributes = NULL;
}
static void __surface_init(ImpObject * object) {
	ImpSurface * s;
	s = IMP_SURFACE(object);

	s->faces = NULL;
	s->edges = NULL;
	s->vertices = NULL;
}
/* ========================================================================== */

int __compare_vertex(void * v1, void * v2) {

	ImpPoint * p1;
	ImpPoint * p2;
	real x, y;

	p1 = IMP_POINT(v1);
	p2 = IMP_POINT(v2);

	y = p1->y - p2->y;
	
	if (y != 0) return (int)y;

	x = p1->x - p2->x;
	return (int)x;
}
/* ========================================================================== */

ImpSurfaceClass * imp_surface_get_class(void) {
	static ImpSurfaceClass * klass = NULL;

	if (klass == NULL) {
		ImpObjectClassInfo surface_info = {
										"ImpSurface",
										sizeof (ImpSurface),
										sizeof (ImpSurfaceClass),
										(ImpObjectClassInitFunc) __surface_class_init,
										(ImpObjectInitFunc) __surface_init,
										(ImpArgSetFunc) NULL,
										(ImpArgGetFunc) NULL
		};

		ImpObjectClass * parent_class = imp_object_get_class();
		ImpObjectClass * surface_class = imp_object_class_new(parent_class, &surface_info);
		klass = IMP_SURFACE_CLASS(surface_class);
	}
	return klass;
}
ImpSurface * imp_surface_new(void) {
	ImpSurface * s;

	s = IMP_SURFACE(imp_object_new(IMP_OBJECT_CLASS(imp_surface_get_class())));

	s->vertices = list_new();
	s->edges = list_new();
	s->faces = list_new();

	return s;
}
/* ========================================================================== */


ImpVertex * imp_surface_add_vertex(ImpSurface * s, ImpVertex * v) {

	list_insert_sorted(s->vertices, v, __compare_vertex);

	return v;
}
ImpEdge * imp_surface_add_edge(ImpSurface * s, ImpEdge * e) {
	list_append(s->edges, e);

	/*
		printf("Adicionando edge: ");
		imp_edge_print(e);
		printf("\n");
	 */

	return e;
}
ImpTriangle * imp_surface_add_triangle(ImpSurface * s, ImpTriangle * t) {

	list_insert(s->faces, t);

	return t;
}
ImpRectangle * imp_surface_add_rectangle(ImpSurface * s, ImpRectangle * r) {

	list_insert(s->faces, r);

	return r;
}
/* ========================================================================== */

ImpVertex * imp_surface_remove_vertex(ImpSurface * s, ImpVertex * v) {

	ImpVertex * _v;
	ImpEdge * e;
	ImpEdge * _e;
	ListIterator * it;


	_v = list_remove_data(s->vertices, v);
	if (!_v) return NULL;

	it = v->segments->first;

	while (it) {
		e = IMP_EDGE(it->data);
		it = it->next;

		_e = imp_surface_remove_edge(s, e);
		if (_e) imp_object_delete(e);

	}
	return _v;
}
ImpEdge * imp_surface_remove_edge(ImpSurface * s, ImpEdge * e) {

	ImpEdge * _e;
	ImpTriangle * t;
	ImpTriangle * _t;
	ImpSegment * seg;

	ListIterator * it;

	_e = list_remove_data(s->edges, e);
	if (!_e) return NULL;

	it = e->faces->first;
	while (it) {

		t = IMP_TRIANGLE(it->data);
		it = it->next;

		_t = imp_surface_remove_triangle(s, t);
		if (_t) imp_object_delete(_t);
	}

	seg = IMP_SEGMENT(e);
	imp_vertex_remove_segment(seg->v[0], seg);
	imp_vertex_remove_segment(seg->v[1], seg);

	return _e;
}
ImpTriangle * imp_surface_remove_triangle(ImpSurface * s, ImpTriangle * t) {

	ImpTriangle * _t;

	_t = list_remove_data(s->faces, t);
	if (!_t) return NULL;

	imp_edge_remove_triangle(t->e[0], t);
	imp_edge_remove_triangle(t->e[1], t);
	imp_edge_remove_triangle(t->e[2], t);

	return _t;
}
/* ========================================================================== */

void imp_surface_remove_orfans_edges(ImpSurface * surf) {
	ImpEdge * e;
	ListIterator * it;

	it = list_get_first(surf->edges);
	while (it) {
		e = IMP_EDGE(it->data);
		it = it->next;
		if (e->faces->size == 0) {
			imp_surface_remove_edge(surf, e);
			imp_object_delete(e);
		}
	}
}

void imp_surface_remove_orfans_vertices(ImpSurface * surf) {
	ImpVertex * v;
	ListIterator * it;

	it = list_get_first(surf->vertices);
	while (it) {
		v = IMP_VERTEX(it->data);
		it = it->next;
		if (v->segments->size == 0) {
			imp_surface_remove_vertex(surf, v);
			imp_object_delete(v);
		}
	}
}

/* ========================================================================== */
void imp_surface_remove_all_elements(ImpSurface * s) {
	list_remove_all_data(s->vertices);
	list_remove_all_data(s->edges);
	list_remove_all_data(s->faces);
}
void imp_surface_remove_all_elements_full(ImpSurface * s) {

	_return_if_fail(s);
	list_remove_all_data_full(s->faces, (ListDataDeleteFunction) imp_object_delete_function);
	list_remove_all_data_full(s->edges, (ListDataDeleteFunction) imp_object_delete_function);
	list_remove_all_data_full(s->vertices, (ListDataDeleteFunction) imp_object_delete_function);

	/*
		ListIterator * it;

		it = s->faces->first;
		while(it){
			imp_surface_remove_triangle(s, it->data);
			imp_object_delete(it->data);
			it = it->next;
		}
		it = s->edges->first;
		while(it){
			imp_surface_remove_edge(s, it->data);

			imp_object_delete(it->data);

			it = it->next;
		}
		it = s->vertices->first;
		while(it){
			imp_surface_remove_vertex(s, it->data);

			imp_object_delete(it->data);

			it = it->next;
		}

		list_remove_all_data(s->faces);
		list_remove_all_data(s->edges);
		list_remove_all_data(s->vertices);
	 */

	//list_remove_all_data_full(s->faces, (ListDataDeleteFunction) imp_object_delete_function);
	//list_remove_all_data_full(s->edges, (ListDataDeleteFunction) imp_object_delete_function);
	//list_remove_all_data_full(s->vertices, (ListDataDeleteFunction) imp_object_delete_function);
}